package editor.tester;

import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.swing.ImageIcon;
import javax.swing.JButton;
import javax.swing.JEditorPane;
import javax.swing.JLabel;
import javax.swing.JOptionPane;
import javax.swing.JPanel;

import org.jwalk.QuestionEvent;
import org.jwalk.QuestionListener;
import org.jwalk.core.TestCase;
import org.jwalk.out.Answer;
import org.jwalk.out.Confirmation;
import org.jwalk.out.Edition;
import org.jwalk.out.Notification;
import org.jwalk.out.Question;

import editor.JavaEditorPane;
import editor.Context;
import editor.manager.WorkSpaceManager;
import editor.project.ProjectFile;

/**
 * Responds to questions 
 * 
 * @author Mark Dessain
 *
 */
public class QuestionResponseListener implements QuestionListener, ActionListener
{
	private int result = -1;

	private ProjectFile file = null;

	
	private JButton yes = new JButton("Yes");
	private JButton no = new JButton("No");
	private JButton cancel = new JButton("Cancel");
	
	// Load the display area
	private JEditorPane questionContent = WorkSpaceManager.consoleManager.getJwalkQuestion();
	private JPanel questionPanel = WorkSpaceManager.consoleManager.getJwalkPanel();
	private JLabel walkerLabel = new JLabel();
	private ImageIcon walkerActive = new ImageIcon("graphics/walkerActive.gif");
	
	/**
	 * @param file file which the questions relate too
	 */
	public QuestionResponseListener(ProjectFile file)
	{
		this.file = file;
		
		// Create new buttons
		walkerLabel.setIcon(walkerActive);

		// Set up action 
		yes.addActionListener(this);
		no.addActionListener(this);
		cancel.addActionListener(this);
	}
	
	/* (non-Javadoc)
	 * @see org.jwalk.QuestionListener#respond(org.jwalk.QuestionEvent)
	 */
	@Override
	public Answer respond(QuestionEvent event) 
	{
		result = -1;
		
		// Clear question area
		WorkSpaceManager.consoleManager.clearJwalkPanel();
		
		// Get question and its type
		Question question = event.getQuestion();
		Edition edition = question.getEdition();
		
		switch (edition) {
			case CONFIRM_DIALOG :
				setUpQuestion((Confirmation) question);
				setUpQuestionPane();
				return handle((Confirmation) question);
			case NOTIFY_DIALOG :
				return handle ((Notification) question);
			// Can insert other cases later
			default :
				return Answer.QUIT;
		}
	}
		
	/**
	 * @param question
	 * @return
	 */
	private Answer handle(Confirmation question) 
	{
		// Loop until a response is given
		while(result == -1)
		{
			try {
				// Wait a second before trying again
				Thread.sleep(1000);
			} catch (InterruptedException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
			// TODO keep a timer and every 1/2 minutes flag up a joptionpane telling the user JWalk is running
		}
		
		// Remove the question from the box since at this point it has been answered
		questionPanel.removeAll();
		
		// Send the responce
		switch (result) {
			case JOptionPane.YES_OPTION :
				result = -1;
				return Answer.YES;
			case JOptionPane.NO_OPTION :
				result = -1;
				return Answer.NO;
			case JOptionPane.CANCEL_OPTION :
				result = -1;
				return Answer.QUIT;
			default :
				result = -1;
				return Answer.QUIT;
		}
	}

	/**
	 * @param question which is been asked
	 * @return answer to the question
	 */
	private Answer handle(Notification question) 
	{
		if (question.isNotice())
		{
			JOptionPane.showMessageDialog(Context.getJwalkEditor(), question.getContent(), "JWalk Notice", JOptionPane.INFORMATION_MESSAGE);
		}
		else if (question.isWarning())
		{
			JOptionPane.showMessageDialog(Context.getJwalkEditor(), question.getContent(), "JWalk Warning", JOptionPane.WARNING_MESSAGE);
		}
		else if (question.isError())
		{
			JOptionPane.showMessageDialog(Context.getJwalkEditor(), question.getContent(), "JWalk Error", JOptionPane.ERROR_MESSAGE);
		}
		
		return Answer.OK;
	}

	/**
	 * @param question which is been asked
	 * @return answer to the question
	 */	
	private void setUpQuestion(Confirmation question) 
	{
		// Get the sequence for the question
		List<TestCase> sequence = question.getTestResult().getSequence();
		String[] questionLines = question.getContent().split("\n");
		
		// Builds the output display
		StringBuilder outputBuilder = new StringBuilder();
		
		for(int i = 0; i < sequence.size(); i++)
		{
			TestCase test = sequence.get(i);
			
			// If the first character is a tab then append it onto the front
			if(questionLines[i].substring(0,1).equals("\t"))
			{
				outputBuilder.append("\t");
				questionLines[i] = questionLines[i].substring(1);
			}
			
			// Finds the line in the java class which matches the call
			outputBuilder.append("<a href=\"" + file.getAbsolutePath() + ":" + findLineOfTest(test) + "\">");
			outputBuilder.append(questionLines[i]);		
			outputBuilder.append("</a> \n");
		}
		
		// Appends the last result line 
		outputBuilder.append(questionLines[questionLines.length - 1]);
		
		// Sets the JEditorPane text
		questionContent.setText("<pre>" + outputBuilder.toString() + "</pre>");
	}
	
	/**
	 * Sets the question
	 */
	private void setUpQuestionPane()
	{
		// Add to panel
		questionPanel.add(questionContent);
		questionPanel.add(yes);
		questionPanel.add(no);
		questionPanel.add(cancel);
		questionPanel.add(walkerLabel);
		
		WorkSpaceManager.consoleManager.repaint();		
	}
	
	/**
	 * @param test test method or constructor
	 * @return the line in the file where it occurs
	 */
	private int findLineOfTest(TestCase test)
	{
		// List of Parameters
		Class<?>[] parms = null;
		
		// Check to see if the test case is a Method or a Constructor, and then select all the parameter types
		if(test.getOperation() instanceof Method)
		{
			parms = ((Method)test.getOperation()).getParameterTypes();
		}
		else if(test.getOperation() instanceof Constructor<?>)
		{
			parms = ((Constructor<?>)test.getOperation()).getParameterTypes();		
		}

		
		// Create a regular expression for the parameters
		StringBuilder paramRegex = new StringBuilder();
		
		for(Class<?> parm: parms)
		{
			if(paramRegex.length() != 0)
			{
				paramRegex.append("(,*)");
			}
			paramRegex.append("(\\s*)(" + parm.getSimpleName() + ")(\\s*)([a-zA-Z0-9_]*)(\\s*)");
		}
		
		// before, during and after parameters
		String beforeParam = "";
		String param = paramRegex.toString();
		String afterParam = "(\\)(\\s*)(\\n*)(\\s*)(\\{))";
		
		// Methods have a returnType, whereby Constructors do not
		if(test.getOperation() instanceof Method)
		{
			String method = test.getOperation().getName();
			String returnType  = test.getReturnType().getSimpleName();	
			beforeParam = "(" + returnType + ")(\\s*)(" + method+ ")(\\s*)(\\()";    
		}
		else if(test.getOperation() instanceof Constructor<?>)
		{
			String method = test.getOperation().getName();		
			beforeParam = "(" + method+ ")(\\s*)(\\()";    
		}
		
		// Select the editor for the file which is been tested
		int index = WorkSpaceManager.textAreaManager.getIndexOfFile(file);
		JavaEditorPane editor = WorkSpaceManager.textAreaManager.getEditorAt(index);
		
		// Complete a pattern match to try and find the method
		Pattern p = Pattern.compile(beforeParam+param+afterParam,Pattern.CASE_INSENSITIVE | Pattern.DOTALL);
	    Matcher m = p.matcher(editor.getText());
	    
	    // Return the starting line
	    if (m.find())
	    {
	    	return editor.getLine(m.start()) + 1;
	    }
	    
	    // If the pattern can't be matched return -1
	    return -1;
		
	}

	/* (non-Javadoc)
	 * @see java.awt.event.ActionListener#actionPerformed(java.awt.event.ActionEvent)
	 */
	@Override
	public void actionPerformed(ActionEvent e) {
		
		Object source = e.getSource();
		
		// Set the result type for each of the buttons
		if(source == yes)
		{
			 result = JOptionPane.YES_OPTION;
		}
		else if(source == no)
		{
			result = JOptionPane.NO_OPTION;
		}
		else if(source == cancel)
		{
	        result = JOptionPane.CANCEL_OPTION;
		}
	
	}


}
